/* Give a program access to peek and poke, via call to iopl(3) */
/* (c) 2010 Richard George, University of Oxford */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/io.h>
#include <sys/resource.h>

int verbose = 0;

int main(int argc, char *argv[], char *envp[])
{   
   int i=0,j=0;
   char **new_envp=0;

   if (argc<2)
   {
	fprintf(stderr,"%s: Run a program with full IO permissions\n\nUsage: %s <program_to_run> [program_arguments]\n\n",argv[0],argv[0]);	
	exit(1);
   }
   else
   {
        if (verbose>0) fprintf(stderr,"%s: Provide IO permissions to a non-root program\n\n",argv[0]); 
   }

   if (verbose>0) fprintf(stderr,"Prior to reducing permissions:\n   real gid = %d\n   real uid = %d\n   effective gid = %d\n   effective uid = %d\n\n",getgid(),getuid(),getegid(),geteuid());

   if (geteuid()!=0)
   {
       fprintf(stderr,"%s: euid = %d : This program must be run as root. Please use sudo.\n",argv[0],geteuid());
       exit(1);
   }

   if (iopl(3)!=0) 
   {
       fprintf(stderr,"%s: Call to iopl(3) failed.\n\n",argv[0]);
       exit(1);
   }
   else
   {
       if (verbose>0) fprintf(stderr,"%s: Have set IO permissions via iopl(3)\n\n",argv[0]);
   }

  /*--- Give up root permissions ---*/
    setgid( getgid() ) ;
    setuid( getuid() ) ;  

    if (verbose>0) fprintf(stderr,"After reducing permissions:\n   real gid = %d\n   real uid = %d\n   effective gid = %d\n   effective uid = %d\n\n",getgid(),getuid(),getegid(),geteuid());

    if (verbose>0) fprintf(stderr,"%s: Now running '%s'...\n\n",argv[0],argv[1]);

    /*--- copy out the environment, and add a new variable to say that the iopl is set ---*/
  
    while (envp[j]!=0) j++;
    new_envp = (char **) calloc(j+2,sizeof(char *));
    for (i=0;i<j;i++) new_envp[i]=envp[i];
    new_envp[i]="WITH_IOPL=True";
    new_envp[i+1]=0;

    /*--- do an exec to the target program, use argv array from parameter 1 onward.*/

    execve(argv[1], &argv[1],new_envp);

    fprintf(stderr,"%s: Failed to execute target program %s\n",argv[0],argv[1]);
    exit(1);
}
 

